﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Net.Mail;
using System.Text;
using System.Linq;

namespace Kaleida.ServiceMonitor.Framework
{
    public static class StringExtensions
    {
        public static TimeSpan ToTimeSpan(this string text)
        {
            if (text == null) throw new ArgumentNullException("text");

            return DurationParser.ParseTimeSpan(text);
        }

        public static Duration ToDuration(this string text)
        {
            if (text == null) throw new ArgumentNullException("text");

            return DurationParser.ParseDuration(text);
        }

        public static TimeSpan ToTimeSpan(this Duration duration)
        {
            if (duration == null) throw new ArgumentNullException("duration");

            return duration.ToString().ToTimeSpan();
        }

        public static bool IsTimeSpan(this string text)
        {
            if (text == null) throw new ArgumentNullException("text");

            return DurationParser.CanParse(text);
        }

        public static bool IsDuration(this string text)
        {
            if (text == null) throw new ArgumentNullException("text");

            return DurationParser.CanParse(text);
        }

        public static bool IsDecimal(this string value)
        {
            if (value == null) throw new ArgumentNullException("value");

            decimal result;
            return decimal.TryParse(value, out result);
        }

        public static bool IsInteger(this string value)
        {
            if (value == null) throw new ArgumentNullException("value");

            int result;
            return int.TryParse(value, out result);
        }

		public static ScheduledTime ToScheduledTime(this string value)
		{
			if (value == null) throw new ArgumentNullException("value");

			var components = value.Split(':');

			if(components.Count() != 2)
				throw new FormatException(string.Format("Cannot parse '{0}' as a ScheduledTime. Expected string to be in HH:MM format. E.g. 09:32", value));

			int hour;
			if (!int.TryParse(components[0], out hour))
				throw new FormatException(string.Format("Cannot parse '{0}' as a ScheduledTime. Expected hour to be a number", value));

			int minute;
			if (!int.TryParse(components[1], out minute))
				throw new FormatException(string.Format("Cannot parse '{0}' as a ScheduledTime. Expected minute to be a number", value));

			return new ScheduledTime(hour, minute);
		}

    	public static string SurroundWith(this string text, string enclosingText)
        {
            if (text == null) throw new ArgumentNullException("text");
            if (enclosingText == null) throw new ArgumentNullException("enclosingText");

            return enclosingText + text + enclosingText;
        }

        public static string ToFormattedString(this string text)
        {
            if (text == null) throw new ArgumentNullException("text");
            return text.SurroundWith("'");
        }

        public static string ToFormattedString(this int text)
        {
            return text.ToString(CultureInfo.InvariantCulture);
        }

        public static string ToFormattedString(this decimal text)
        {
            return text.ToString(CultureInfo.InvariantCulture);
        }

        public static string ToFormattedString(this TimeSpan timeSpan)
        {
            var duration = timeSpan.Duration();

            var formatted = new StringBuilder();

            if(timeSpan.Ticks < 0)
                formatted.Append("-");

            formatted.Append("[");
            if(duration.Days != 0)
                formatted.AppendFormat("{0} ", duration.Days);

            formatted.AppendFormat("{0:00}:{1:00}:{2:00}", duration.Hours, duration.Minutes, duration.Seconds);

            if (duration.Milliseconds != 0)
                formatted.AppendFormat(".{0:000}", duration.Milliseconds);

            formatted.Append("]");
            return formatted.ToString();
        }

        public static List<KeyValuePair<string, string>> SplitIntoKeyValuePairs(this string argumentsString)
        {
            if (argumentsString == null) throw new ArgumentNullException("argumentsString");

            if (argumentsString == "")
                return new List<KeyValuePair<string, string>>();

            var argumentStrings = argumentsString.Split(',');
            return argumentStrings.Select(BuildKeyValuePair).ToList();
        }

		public static IList<MailAddress> ToMailAddressList(this string text)
		{
			if (text == null) throw new ArgumentNullException("text");

			var emailAddresses = text.Split(';').Select(i => i.Trim()).Where(i => i != "").ToList();

			if (!emailAddresses.Any())
				throw new InvalidOperationException("Must provide at least one email address (multiple addresses can be specified using a semicolon. e.g. \"me@example.com;someone@example.com\")");

			return emailAddresses.Select(BuildMailAddress).ToList();
		}

		private static MailAddress BuildMailAddress(string text)
		{
			try
			{
				return new MailAddress(text);
			}
			catch (FormatException e)
			{
				throw new InvalidOperationException(e.Message + " (multiple addresses can be specified using a semicolon. e.g. \"me@example.com;someone@example.com\")");
			}
		}

        public static IList<ScheduledTime> ToScheduledTimeList(this string text)
        {
            if (text == null) throw new ArgumentNullException("text");

            var timeStrings = text.Split(';').Select(i => i.Trim()).Where(i => i != "").ToList();

            if (!timeStrings.Any())
                throw new InvalidOperationException("Must provide at least one scheduled time (multiple addresses can be specified using a semicolon. e.g. \"14:30; 17:15\")");

            return timeStrings.Select(BuildScheduledTime).ToList();
        }

        private static ScheduledTime BuildScheduledTime(string text)
        {
            try
            {
                return text.ToScheduledTime();
            }
            catch (FormatException e)
            {
                throw new InvalidOperationException(e.Message + " (multiple times can be specified using a semicolon. e.g. \"14:30; 17:15\")");
            }
        }

        private static KeyValuePair<string, string> BuildKeyValuePair(string argumentString)
        {
            var components = argumentString.Split('=');

            if (components.Length != 2)
                throw new InvalidOperationException(String.Format("Expected argument '{0}' to be specified as ARG=VAL", argumentString));

            return new KeyValuePair<string, string>(components[0], components[1]);
        }
    }
}
